package com.lanyam.travel.base.controller;


import com.lanyam.travel.base.utils.AESUtil;
import com.lanyam.travel.base.utils.Constant;
import com.lanyam.travel.base.utils.JSONResult;
import com.lanyam.travel.base.entity.OrderRefund;
import com.lanyam.travel.base.entity.OrderRefundDetails;
import com.lanyam.travel.base.entity.TravelOrder;
import com.lanyam.travel.base.entity.vo.OrderRefundVo;
import com.lanyam.travel.base.entity.vo.RefundRecordVo;
import com.lanyam.travel.base.entity.vo.TravelOrderVo;
import com.lanyam.travel.base.service.IOrderRefundDetailsService;
import com.lanyam.travel.base.service.IOrderRefundService;
import com.lanyam.travel.base.service.ITravelOrderDetailsService;
import com.lanyam.travel.base.service.ITravelOrderService;
import com.lanyam.travel.base.utils.JsonMapper;
import com.lanyam.travel.base.utils.PayUtil;

import org.springframework.web.bind.annotation.*;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.security.KeyStore;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.UUID;

import javax.net.ssl.SSLContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.apache.log4j.Logger;
import org.bouncycastle.asn1.dvcs.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import com.fasterxml.jackson.core.type.TypeReference;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;


@RestController
@RequestMapping("/orderRefund")
public class OrderRefundController {

    @Autowired
    private IOrderRefundService orderRefundService;
    
    @Autowired
	private ITravelOrderService travelOrderService;
    
    @Autowired
    private IOrderRefundDetailsService orderRefundDetailService;
    
    @Autowired
    private HttpServletRequest request;

    /**
     * 分页查询数据
     */
    @GetMapping("/getPage")
    public IPage<OrderRefundVo> getPage (String queryMap,
                                                @RequestParam(name="page", defaultValue = "1") Integer page,
                                                @RequestParam(name="rows", defaultValue = "10") Integer rows) {
        Map<String, Object> query = JsonMapper.string2Obj(queryMap,new TypeReference<Map<String, Object>>() {});
        IPage<OrderRefundVo> pageInfo = orderRefundService.getPageOrderRefund(query, page, rows);
        return pageInfo;
    }
    /**
     * 后台的退款订单列表分页
     * @param reqMap 必填参数：page,pageSize
     * @return
     */
    @PostMapping("/queryOrderRefundByPage")
    public JSONResult queryOrderRefundByPage(@RequestBody(required = false) Map<String,Object> reqMap)
    {
    	Integer page = (Integer) reqMap.get("page");
		 Integer pageSize = (Integer) reqMap.get("pageSize");
		 if(StringUtils.isEmpty(page)||StringUtils.isEmpty(pageSize))
		 {
			 return JSONResult.errorMsg("参数不全");
		 }
		 Page<OrderRefundVo> ipage = orderRefundService.queryOrderRefundByPage(page, pageSize,reqMap);
			
		 return JSONResult.ok(ipage.getRecords());
    }

	/**
	 * 发起退款申请
	 * @param refundRecord
	 * @return
	 */
    @RequestMapping(value = "/requireRefund", method = RequestMethod.POST, produces = "application/json;charset=UTF-8")
	public JSONResult requireRefund(@RequestBody RefundRecordVo refundRecordVo)
	{
		if(null == refundRecordVo || StringUtils.isEmpty(refundRecordVo.getOrderId())
				|| StringUtils.isEmpty(refundRecordVo.getRequireUserId()))
		{
			return JSONResult.errorMsg("参数不全");
		}
		TravelOrder order = travelOrderService.getOrderById(refundRecordVo.getOrderId());
		//订单处于已支付并且未使用才可以退款
		if(order.getPayStatus() == 0)
		{
			return JSONResult.errorMsg("订单尚未支付，不能申请退款");
		}
		else if(order.getPayStatus() == 2)
		{
			OrderRefund record = orderRefundService.queryOrderRefundByOrderId(refundRecordVo.getOrderId());
			if(null != record)
			{
				if(record.getRefundStatus() == 0)
				{
					return JSONResult.errorMsg("订单已申请退款，正在审核过程中，请耐心等待");
				}
				else if(record.getRefundStatus() == 1)
				{
					return JSONResult.errorMsg("订单退款申请已通过，请等待银行操作");
				}
				else if(record.getRefundStatus() == -1)
				{
					//同一订单再次申请退款
					record.setRefundStatus(0);
                    record.setRequireUserid(refundRecordVo.getRequireUserId());
					orderRefundService.updateOrderRefund(record);
					//创建退款审核明细，每申请一次退款，添加一次记录
					OrderRefundDetails detail = new OrderRefundDetails();
					detail.setId(UUID.randomUUID().toString().replace("-", ""));
					detail.setRefundId(record.getId());
					detail.setRequireUserId(refundRecordVo.getRequireUserId());
					detail.setRequireTime(new Date());
					detail.setReason(refundRecordVo.getReason());
					detail.setRemarks(refundRecordVo.getRemark());
					orderRefundDetailService.addOrderRefundDetails(detail);
					return JSONResult.ok("申请退款已重新提交，请耐心等待审核结果。");
				}
				else
				{
					return JSONResult.errorMsg("订单退款已完成，不能再次申请退款。");
				}
			}
		}
		order.setPayStatus(2);
		travelOrderService.updateOrder(order);
		//前台传值：订单ID/退款总金额/数量/申请退款的用户id/退款原因/退款说明
		//后台赋值：退款创建时间/退款完成情况/
		OrderRefund orderRefund = new OrderRefund();
		String refundId = UUID.randomUUID().toString().replace("-", "");
		orderRefund.setId(refundId);
		orderRefund.setCreateTime(new Date());
		orderRefund.setRefundStatus(0);
        orderRefund.setOrderId(refundRecordVo.getOrderId());
		orderRefund.setRefundAmout(new BigDecimal(refundRecordVo.getRefundCost()));
        orderRefund.setRefundPrice(new BigDecimal(refundRecordVo.getRefundCost()));
        orderRefund.setRefundTotal(1);
        orderRefund.setRequireUserid(refundRecordVo.getRequireUserId());
		orderRefundService.addOrderRefund(orderRefund);
		//创建退款审核明细，用于审核
		OrderRefundDetails detail = new OrderRefundDetails();
		detail.setId(UUID.randomUUID().toString().replace("-", ""));
		detail.setRefundId(refundId);
		detail.setRequireUserId(refundRecordVo.getRequireUserId());
		detail.setRequireTime(new Date());
		detail.setReason(refundRecordVo.getReason());
		detail.setRemarks(refundRecordVo.getRemark());
		orderRefundDetailService.addOrderRefundDetails(detail);
		return JSONResult.ok("退款申请已受理，请等待审核结果。");
	}

    /**
     * 编辑数据
     */
    @PostMapping("/update")
    public JSONResult update (@RequestBody OrderRefund orderRefund) {
        JSONResult result = null;
        try{
            orderRefundService.updateOrderRefund(orderRefund);
            result = JSONResult.ok();
        }  catch (Exception e) {
            e.printStackTrace();
            String msg = e.getMessage();
            result = JSONResult.errorMsg(msg);
        }
        return result;
    }

    /**
     * 删除数据
     */
    @PostMapping("/delById")
    public JSONResult delById (String id) {
        JSONResult result = null;
        try {
            orderRefundService.delById(id);
            result = JSONResult.ok();
        }  catch (Exception e) {
            e.printStackTrace();
            String msg = e.getMessage();
            result = JSONResult.errorMsg(msg);
        }
        return result;
    }
    
    /**
     * 获取退款审核记录
     * @param refundId
     * @return
     */
    @PostMapping("/getVerifyRecords")
    public JSONResult getVerifyRecords(String refundId)
    {
    	List<OrderRefundDetails> verifyRecords = orderRefundDetailService.getVerifyRecords(refundId);
    	return JSONResult.ok(verifyRecords);
    }
    
    /**
     * 退款申请审核失败，默认修改最后一次退款审核的记录
     * @param refundId
     * @param verifyUserId 审核人id
     * @param reason 审核未通过原因
     * @return
     */
    @PostMapping("/refundVerifyNG")
    public JSONResult refundVerifyNG(String refundId,String verifyUserId,String reason)
    {
    	//修改退款申请的状态
    	OrderRefund refund = orderRefundService.getOrderRefundById(refundId);
    	refund.setRefundStatus(-1);
    	orderRefundService.updateOrderRefund(refund);
    	//获取最后一次审核记录
    	OrderRefundDetails detail = orderRefundDetailService.getLatestRefundDeatail(refundId);
    	detail.setVerifyFailReason(reason);
    	detail.setVerifyUserId(verifyUserId);
    	detail.setVerifyResult(0);
    	detail.setVerifyTime(new Date());
    	orderRefundDetailService.updateOrderRefundDetails(detail);
    	return JSONResult.ok();
    }
    
    /**
     * 退款申请审核通过，涉及到退款转账
     * @param refundId
     * @param verifyUserId 审核人id
     * @return
     */
    @Transactional(rollbackFor=Exception.class)
    @PostMapping("/verifyRefundRecordOk")
    public JSONResult verifyRefundRecordOk(String refundId,String verifyUserId)
    {
    	//修改退款申请的状态
    	OrderRefund refund = orderRefundService.getOrderRefundById(refundId);
    	refund.setRefundStatus(2);
    	orderRefundService.updateOrderRefund(refund);
    	//获取最后一次审核记录,审核记录在申请退款时创建
    	OrderRefundDetails detail = orderRefundDetailService.getLatestRefundDeatail(refundId);
    	detail.setVerifyUserId(verifyUserId);
    	detail.setVerifyResult(1);
    	detail.setVerifyTime(new Date());
    	orderRefundDetailService.updateOrderRefundDetails(detail);
    	//TODO 银行退款操作
    	Logger log = Logger.getLogger(OrderRefundController.class);
		//设置最终返回对象
        JSONObject resultJson = new JSONObject();
        //接受参数(金额)
        String amount = refund.getRefundAmout().toString();
        //接受参数(订单Id)
        String orderId = refund.getOrderId();
        //接受参数(商品ID)
//        TravelOrder order = travelOrderService.getOrderById(orderId);
//        String productId = order.get
        //接受参数(商品sku)
//        String sku = request.getParameter("sku");
        //接受参数(商品name)
//        String name = request.getParameter("name");
        //接受参数(商品订单号)
//        String incrementId = request.getParameter("incrementId");
        
        //创建hashmap(用户获得签名)
        SortedMap<String, String> paraMap = new TreeMap<String, String>();
        //设置随机字符串
        String nonceStr = UUID.randomUUID().toString().replaceAll("-", "");
        //设置商户退款单号
        Integer randomNumber = new Random().nextInt(900)+ 100;
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        String orderIncrementId = sdf.format(new Date())+randomNumber;
        
        //设置请求参数(小程序ID)
        paraMap.put("appid", Constant.APP_ID);
        //设置请求参数(商户号)
        paraMap.put("mch_id", Constant.MCH_ID);
        //设置请求参数(随机字符串)
        paraMap.put("nonce_str", nonceStr);
        //设置请求参数(商户订单号),优先微信订单号，其次才是自定义的订单号
        paraMap.put("out_trade_no", orderId);
        //设置请求参数(商户退款单号)
        paraMap.put("out_refund_no", orderIncrementId);
        //设置请求参数(订单金额)
        paraMap.put("total_fee", amount);
        //设置请求参数(退款金额)
        paraMap.put("refund_fee", amount);
        //TODO (这个回调地址 没有具体进行测试 需要写好逻辑 打版在测试)设置请求参数(通知地址)
        paraMap.put("notify_url", Constant.REFUND_CALLBACK_URL);
        //调用逻辑传入参数按照字段名的 ASCII 码从小到大排序（字典序）
        String stringA = PayUtil.createLinkString(paraMap);
        //第二步，在stringA最后拼接上key得到stringSignTemp字符串，并对stringSignTemp进行MD5运算，再将得到的字符串所有字符转换为大写，得到sign值signValue。(签名)
//        String sign = MD5Util.MD5(stringA+"&key="+Configuration.KEY).toUpperCase();
        String sign = PayUtil.sign(stringA, Constant.key, "utf-8").toUpperCase();
        //将参数 编写XML格式
        StringBuffer paramBuffer = new StringBuffer();
        paramBuffer.append("<xml>");
        paramBuffer.append("<appid>"+Constant.APP_ID+"</appid>");
        paramBuffer.append("<mch_id>"+Constant.MCH_ID+"</mch_id>");
        paramBuffer.append("<nonce_str>"+paraMap.get("nonce_str")+"</nonce_str>");
        paramBuffer.append("<sign>"+sign+"</sign>");
        paramBuffer.append("<out_refund_no>"+paraMap.get("out_refund_no")+"</out_refund_no>");
        paramBuffer.append("<out_trade_no>"+paraMap.get("out_trade_no")+"</out_trade_no>");
        paramBuffer.append("<refund_fee>"+paraMap.get("refund_fee")+"</refund_fee>");
        paramBuffer.append("<total_fee>"+paraMap.get("total_fee")+"</total_fee>");
        paramBuffer.append("<notify_url>"+paraMap.get("notify_url")+"</notify_url>");
        paramBuffer.append("</xml>");
        
        try {
            //发送请求(POST)(获得数据包ID)(这有个注意的地方 如果不转码成ISO8859-1则会告诉你body不是UTF8编码 就算你改成UTF8编码也一样不好使 所以修改成ISO8859-1)
        	String result = doRefund(Constant.REFUND_URL, new String(paramBuffer.toString().getBytes(), "ISO8859-1"));
            Map<String,String> map = PayUtil.doXMLParse(result);
            //应该创建 退款表数据
            if(null!=map && !StringUtils.isEmpty(map.get("return_code")) && "SUCCESS".equals(map.get("return_code")))
            {
                if(StringUtils.isEmpty(map.get("err_code_des")))
                {
                	//接口调用成功 执行操作逻辑 返回成功状态码给前台
                	//TODO
                	
                }else {
                    resultJson.put("returnCode", "error");
                    resultJson.put("err_code_des", map.get("err_code_des"));
                }
            }else {
                resultJson.put("returnCode", map.get("return_code"));
                resultJson.put("err_code_des", map.get("err_code_des"));
            }
        } catch (UnsupportedEncodingException e) {
            log.info("微信 退款 异常："+e.getMessage());
            e.printStackTrace();
        } catch (Exception e) {
            log.info("微信 退款 异常："+e.getMessage());
            e.printStackTrace();
        }
        log.info("微信 退款 失败");
		return JSONResult.ok(resultJson);
    }
    
	/**
	 * 发送退款请求
	 * @param request
	 * @param url
	 * @param data
	 * @return
	 * @throws Exception
	 */
	private String doRefund(String url,String data) throws Exception{
        /**
         * 注意PKCS12证书 是从微信商户平台-》账户设置-》 API安全 中下载的
         */
        
        KeyStore keyStore  = KeyStore.getInstance("PKCS12");
        String substring = request.getSession().getServletContext().getRealPath("/")
        				.substring(0, request.getSession().getServletContext().getRealPath("/")
        				.lastIndexOf("webapp\\"));
        FileInputStream instream = new FileInputStream(substring+"resources/refund_certificate/apiclient_cert.p12");//P12文件目录 证书路径,待替换
        try {
            /**
             * 此处要改
             * */
            keyStore.load(instream, Constant.MCH_ID.toCharArray());//这里写密码..默认是你的MCHID
        } finally {
            instream.close();
        }
 
        // Trust own CA and all self-signed certs
        /**
         * 此处要改
         * */
        SSLContext sslcontext = SSLContexts.custom()
                .loadKeyMaterial(keyStore, Constant.MCH_ID.toCharArray())//这里也是写密码的  
                .build();
        // Allow TLSv1 protocol only
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                sslcontext,
                new String[] { "TLSv1" },
                null,
                SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
        CloseableHttpClient httpclient = HttpClients.custom()
                .setSSLSocketFactory(sslsf)
                .build();
        try {
            HttpPost httpost = new HttpPost(url); // 设置响应头信息
            httpost.addHeader("Connection", "keep-alive");
            httpost.addHeader("Accept", "*/*");
            httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
            httpost.addHeader("Host", "api.mch.weixin.qq.com");
            httpost.addHeader("X-Requested-With", "XMLHttpRequest");
            httpost.addHeader("Cache-Control", "max-age=0");
            httpost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
            httpost.setEntity(new StringEntity(data, "UTF-8"));
            CloseableHttpResponse response = httpclient.execute(httpost);
            try {
                HttpEntity entity = response.getEntity();
 
                String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8");
                EntityUtils.consume(entity);
               return jsonStr;
            } finally {
                response.close();
            }
        } finally {
            httpclient.close();
        }
    }
	/**
	 * 退款后的回调函数
	 * @param request
	 * @param response
	 */
	@RequestMapping("/refundCallback")
	public void refundCallback(HttpServletRequest request,HttpServletResponse response)
	{
		Logger log = Logger.getLogger(OrderRefundController.class);
        log.info("退款  微信回调接口方法 start");
        String inputLine = "";
        String notityXml = "";
        try {
        	 while((inputLine = request.getReader().readLine()) != null)
             {
                 notityXml += inputLine;
             }
             //关闭流
             request.getReader().close();
             log.info("退款  微信回调内容信息："+notityXml);
             //解析成Map
             Map<String,String> map = PayUtil.doXMLParse(notityXml);
             //判断 退款是否成功
             if("SUCCESS".equals(map.get("return_code")))
             {
                 log.info("退款  微信回调返回是否退款成功：是");
                 //获得 返回的商户订单号
                 String passMap = AESUtil.decryptData(map.get("req_info"));
                 //拿到解密信息
                 map = PayUtil.doXMLParse(passMap);
                 //拿到解密后的订单号
                 String outTradeNo = map.get("out_trade_no");
                 
                 log.info("退款  微信回调返回商户订单号："+map.get("out_trade_no"));
                 //支付成功 修改订单状态 通知微信成功回调
//                 int sqlRow = orderJpaDao.updateOrderStatus("refunded",new Timestamp(System.currentTimeMillis()), outTradeNo);
//                 if(sqlRow == 1) {
//                     log.info("退款 微信回调 更改订单状态成功");
//                 }
             }
             else 
             {
                 //获得 返回的商户订单号
                 String passMap = AESUtil.decryptData(map.get("req_info"));
                 //拿到解密信息
                 map = PayUtil.doXMLParse(passMap);
                 //拿到解密后的订单号
                 String outTradeNo = map.get("out_trade_no");
                 //更改 状态为取消
//                 int sqlRow = orderJpaDao.updateOrderStatus("canceled",new Timestamp(System.currentTimeMillis()), outTradeNo);
//                 if(sqlRow == 1) {
//                     log.info("退款 微信回调返回是否退款成功：否");
//                 }
             }
             response.setContentType("text/xml");
             //给微信服务器返回 成功标示 否则会一直询问 咱们服务器 是否回调成功
             PrintWriter writer = response.getWriter();
             //封装 返回值
             StringBuffer buffer = new StringBuffer();
             buffer.append("<xml>");
             buffer.append("<return_code>SUCCESS</return_code>"); 
             buffer.append("<return_msg>OK</return_msg>"); 
             buffer.append("</xml>"); 
             //返回  
             writer.print(buffer.toString());
		} catch (IOException  e) {
			// TODO: handle exception
			e.printStackTrace();
		}
        catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}
           
	}
}